home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
nethack.lha
/
nethack-3.1
/
src
/
eat.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-18
|
47KB
|
1,868 lines
/* SCCS Id: @(#)eat.c 3.1 92/12/06 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
/*#define DEBUG /* uncomment to enable new eat code debugging */
#ifdef DEBUG
# ifdef WIZARD
#define debugpline if (wizard) pline
# else
#define debugpline pline
# endif
#endif
STATIC_PTR int NDECL(eatmdone);
STATIC_PTR int NDECL(eatfood);
STATIC_PTR int NDECL(opentin);
STATIC_PTR int NDECL(unfaint);
#ifdef OVLB
static void FDECL(choke, (struct obj *));
static void NDECL(recalc_wt);
static struct obj *FDECL(touchfood, (struct obj *));
static void NDECL(do_reset_eat);
static void FDECL(done_eating, (BOOLEAN_P));
static void FDECL(cprefx, (int));
static int FDECL(intrinsic_possible, (int,struct permonst *));
static void FDECL(givit, (int,struct permonst *));
static void FDECL(cpostfx, (int));
static void FDECL(start_tin, (struct obj *));
static int FDECL(eatcorpse, (struct obj *));
static void FDECL(start_eating, (struct obj *));
static void FDECL(fprefx, (struct obj *));
static void FDECL(fpostfx, (struct obj *));
static int NDECL(bite);
#ifdef POLYSELF
static int FDECL(rottenfood, (struct obj *));
static void NDECL(eatspecial);
static void FDECL(eatring, (struct obj *));
static const char * FDECL(foodword, (struct obj *));
#else
static int NDECL(rottenfood);
#endif /* POLYSELF */
char corpsename[60];
char msgbuf[BUFSZ];
#endif /* OVLB */
/* hunger texts used on bottom line (each 8 chars long) */
#define SATIATED 0
#define NOT_HUNGRY 1
#define HUNGRY 2
#define WEAK 3
#define FAINTING 4
#define FAINTED 5
#define STARVED 6
#ifdef OVLB
const char *hu_stat[] = {
"Satiated",
" ",
"Hungry ",
"Weak ",
"Fainting",
"Fainted ",
"Starved "
};
#endif /* OVLB */
#ifndef OVLB
STATIC_DCL const char NEARDATA comestibles[];
#ifdef POLYSELF
STATIC_OVL const char NEARDATA allobj[];
#endif /* POLYSELF */
#else
STATIC_OVL const char NEARDATA comestibles[] = { FOOD_CLASS, 0 };
#ifdef POLYSELF
/* Gold must come first for getobj(). */
STATIC_OVL const char NEARDATA allobj[] = {
GOLD_CLASS, WEAPON_CLASS, ARMOR_CLASS, POTION_CLASS, SCROLL_CLASS,
WAND_CLASS, RING_CLASS, AMULET_CLASS, FOOD_CLASS, TOOL_CLASS,
GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, SPBOOK_CLASS, 0 };
#endif /* POLYSELF */
#endif /* OVLB */
#ifdef OVL1
# ifdef POLYSELF
boolean
is_edible(obj)
register struct obj *obj;
{
if (metallivorous(uasmon) && is_metallic(obj))
return TRUE;
if (u.umonnum == PM_GELATINOUS_CUBE && is_organic(obj))
return TRUE;
return !!index(comestibles, obj->oclass);
}
# endif /* POLYSELF */
#endif /* OVL1 */
#ifdef OVLB
void
init_uhunger(){
u.uhunger = 900;
u.uhs = NOT_HUNGRY;
}
static const struct { const char *txt; int nut; } tintxts[] = {
{"deep fried", 60},
{"pickled", 40},
{"soup made from", 20},
{"pureed", 500},
{"rotten", -50},
{"homemade", 50},
{"", 0}
};
#define TTSZ SIZE(tintxts)
static struct {
struct obj *tin;
int usedtime, reqtime;
} NEARDATA tin;
static struct {
struct obj *piece; /* the thing being eaten, or last thing that
* was partially eaten, unless that thing was
* a tin, which uses the tin structure above */
int usedtime, /* turns spent eating */
reqtime; /* turns required to eat */
int nmod; /* coded nutrition per turn */
Bitfield(canchoke,1); /* was satiated at beginning */
Bitfield(fullwarn,1); /* have warned about being full */
Bitfield(eating,1); /* victual currently being eaten */
Bitfield(doreset,1); /* stop eating at end of turn */
} NEARDATA victual;
STATIC_PTR
int
eatmdone() { /* called after mimicing is over */
u.usym =
#ifdef POLYSELF
u.mtimedone ? uasmon->mlet :
#endif
S_HUMAN;
newsym(u.ux,u.uy);
return 0;
}
/* Created by GAN 01/28/87
* Amended by AKP 09/22/87: if not hard, don't choke, just vomit.
* Amended by 3. 06/12/89: if not hard, sometimes choke anyway, to keep risk.
* 11/10/89: if hard, rarely vomit anyway, for slim chance.
*/
/*ARGSUSED*/
static void
choke(food) /* To a full belly all food is bad. (It.) */
register struct obj *food;
{
/* only happens if you were satiated */
if(u.uhs != SATIATED) return;
if (pl_character[0] == 'K' && u.ualign.type == A_LAWFUL)
u.ualign.record--; /* gluttony is unchivalrous */
if (!rn2(20)) {
You("stuff yourself and then vomit voluminously.");
morehungry(1000); /* you just got *very* sick! */
vomit();
} else {
killer_format = KILLED_BY_AN;
/*
* Note all "killer"s below read "Choked on %s" on the
* high score list & tombstone. So plan accordingly.
*/
if(food) {
#ifdef POLYSELF
You("choke over your %s.", foodword(food));
if (food->oclass == GOLD_CLASS) {
killer_format = KILLED_BY;
killer = "eating too rich a meal";
} else {
#else
You("choke over your food.");
#endif
killer = singular(food, xname);
#ifdef POLYSELF
}
#endif
} else {
You("choke over it.");
killer = "quick snack";
}
You("die...");
done(CHOKING);
}
}
static void
recalc_wt() { /* modify object wt. depending on time spent consuming it */
register struct obj *piece = victual.piece;
#ifdef DEBUG
debugpline("Old weight = %d", piece->owt);
debugpline("Used time = %d, Req'd time = %d",
victual.usedtime, victual.reqtime);
#endif
/* weight(piece) = weight of full item */
if(victual.usedtime)
piece->owt = eaten_stat(weight(piece), piece);
#ifdef DEBUG
debugpline("New weight = %d", piece->owt);
#endif
}
void
reset_eat() { /* called when eating interrupted by an event */
/* we only set a flag here - the actual reset process is done after
* the round is spent eating.
*/
if(victual.eating && !victual.doreset) {
#ifdef DEBUG
debugpline("reset_eat...");
#endif
victual.doreset = TRUE;
}
return;
}
void
bill_dummy_object(otmp)
register struct obj *otmp;
{
/* Create a dummy duplicate to put on bill. The duplicate exists
* only in the billobjs chain. This function is used when a store
* object is being altered, and a copy of the original is needed
* for billing purposes.
*/
register struct obj *dummy;
if(otmp->unpaid)
subfrombill(otmp, shop_keeper(*u.ushops));
dummy = newobj(otmp->onamelth);
*dummy = *otmp;
dummy->o_id = flags.ident++;
dummy->owt = weight(dummy);
if(otmp->onamelth)
(void)strncpy(ONAME(dummy),ONAME(otmp), (int)otmp->onamelth);
if(Is_candle(dummy)) dummy->lamplit = 0;
addtobill(dummy, FALSE, TRUE, TRUE);
}
static struct obj *
touchfood(otmp)
register struct obj *otmp;
{
if (otmp->quan > 1L) {
if(!carried(otmp))
(void) splitobj(otmp, 1L);
else
otmp = splitobj(otmp, otmp->quan - 1L);
#ifdef DEBUG
debugpline("split object,");
#endif
}
if (!otmp->oeaten) {
if(((!carried(otmp) && costly_spot(otmp->ox, otmp->oy) &&
saleable(rooms[*u.ushops-ROOMOFFSET].rtype-SHOPBASE, otmp))
|| otmp->unpaid) &&
(otmp->otyp == CORPSE || objects[otmp->otyp].oc_delay > 1)) {
/* create a dummy duplicate to put on bill */
You("bite it, you bought it!");
bill_dummy_object(otmp);
}
otmp->oeaten = (otmp->otyp == CORPSE ?
(int)mons[otmp->corpsenm].cnutrit :
objects[otmp->otyp].oc_nutrition);
}
if (carried(otmp)) {
freeinv(otmp);
if(inv_cnt() >= 52)
dropy(otmp);
else
otmp = addinv(otmp); /* unlikely but a merge is possible */
}
return(otmp);
}
/* When food decays, in the middle of your meal, we don't want to dereference
* any dangling pointers, so set it to null (which should still trigger
* do_reset_eat() at the beginning of eatfood()) and check for null pointers
* in do_reset_eat().
*/
void
food_disappears(obj)
register struct obj *obj;
{
if (obj == victual.piece) victual.piece = (struct obj *)0;
}
static void
do_reset_eat() {
#ifdef DEBUG
debugpline("do_reset_eat...");
#endif
if (victual.piece) {
victual.piece = touchfood(victual.piece);
recalc_wt();
}
victual.fullwarn = victual.eating = victual.doreset = FALSE;
/* Do not set canchoke to FALSE; if we continue eating the same object
* we need to know if canchoke was set when they started eating it the
* previous time. And if we don't continue eating the same object
* canchoke always gets recalculated anyway.
*/
stop_occupation();
}
STATIC_PTR
int
eatfood() { /* called each move during eating process */
if(!carried(victual.piece) && !obj_here(victual.piece, u.ux, u.uy)) {
/* maybe it was stolen? */
do_reset_eat();
return(0);
}
if(!victual.eating) return(0);
if(++victual.usedtime < victual.reqtime) {
if(bite()) return(0);
return(1); /* still busy */
} else { /* done */
done_eating(TRUE);
return(0);
}
}
static void
done_eating(message)
boolean message;
{
#ifndef NO_SIGNAL
victual.piece->in_use = TRUE;
#endif
if (nomovemsg) {
if (message) pline(nomovemsg);
nomovemsg = 0;
} else if (message)
You("finish eating %s.", the(singular(victual.piece, xname)));
if(victual.piece->otyp == CORPSE)
cpostfx(victual.piece->corpsenm);
else
fpostfx(victual.piece);
if (carried(victual.piece)) useup(victual.piece);
else useupf(victual.piece);
victual.piece = (struct obj *) 0;
victual.fullwarn = victual.eating = victual.doreset = FALSE;
}
static void
cprefx(pm)
register int pm;
{
if ((pl_character[0]=='E') ? is_elf(&mons[pm]) : is_human(&mons[pm])) {
You("cannibal! You will regret this!");
Aggravate_monster |= FROMOUTSIDE;
}
switch(pm) {
case PM_LITTLE_DOG:
case PM_DOG:
case PM_LARGE_DOG:
case PM_KITTEN:
case PM_HOUSECAT:
case PM_LARGE_CAT:
You("feel that eating the %s was a bad idea.", mons[pm].mname);
Aggravate_monster |= FROMOUTSIDE;
break;
case PM_COCKATRICE:
case PM_MEDUSA:
#ifdef POLYSELF
if(!resists_ston(uasmon))
if(!(poly_when_stoned(uasmon) && polymon(PM_STONE_GOLEM)))
#endif
{
char *cruft; /* killer is const char * */
killer_format = KILLED_BY;
killer = cruft = (char *) /* sizeof "s" includes \0 */
alloc((unsigned) strlen(mons[pm].mname)
+ sizeof " meat");
Sprintf(cruft, "%s meat", mons[pm].mname);
You("turn to stone.");
done(STONING);
}
break;
case PM_LIZARD:
/* Relief from cockatrices -dgk */
if (Stoned) {
Stoned = 0;
You("feel limber!");
}
break;
case PM_DEATH:
case PM_PESTILENCE:
case PM_FAMINE:
{ char buf[BUFSZ];
pline("Eating that is instantly fatal.");
Sprintf(buf, "unwisely ate the body of %s",
mons[pm].mname);
killer = buf;
killer_format = NO_KILLER_PREFIX;
done(DIED);
/* It so happens that since we know these monsters */
/* cannot appear in tins, victual.piece will always */
/* be what we want, which is not generally true. */
revive_corpse(victual.piece, 0, FALSE);
return;
}
default:
if(acidic(&mons[pm]) && Stoned) {
pline("What a pity - you just destroyed a future piece of art!");
Stoned = 0;
}
}
return;
}
/*
* If you add an intrinsic that can be gotten by eating a monster, add it
* to intrinsic_possible() and givit(). (It must already be in prop.h to
* be an intrinsic property.)
* It would be very easy to make the intrinsics not try to give you one
* that you already had by checking to see if you have it in
* intrinsic_possible() instead of givit().
*/
/* intrinsic_possible() returns TRUE iff a monster can give an intrinsic. */
static int
intrinsic_possible(type, ptr)
int type;
register struct permonst *ptr;
{
switch (type) {
case FIRE_RES:
#ifdef DEBUG
if (ptr->mconveys & MR_FIRE) {
debugpline("can get fire resistance");
return(TRUE);
} else return(FALSE);
#else
return(ptr->mconveys & MR_FIRE);
#endif
case SLEEP_RES:
#ifdef DEBUG
if (ptr->mconveys & MR_SLEEP) {
debugpline("can get sleep resistance");
return(TRUE);
} else return(FALSE);
#else
return(ptr->mconveys & MR_SLEEP);
#endif
case COLD_RES:
#ifdef DEBUG
if (ptr->mconveys & MR_COLD) {
debugpline("can get cold resistance");
return(TRUE);
} else return(FALSE);
#else
return(ptr->mconveys & MR_COLD);
#endif
case DISINT_RES:
#ifdef DEBUG
if (ptr->mconveys & MR_DISINT) {
debugpline("can get disintegration resistance");
return(TRUE);
} else return(FALSE);
#else
return(ptr->mconveys & MR_DISINT);
#endif
case SHOCK_RES: /* shock (electricity) resistance */
#ifdef DEBUG
if (ptr->mconveys & MR_ELEC) {
debugpline("can get shock resistance");
return(TRUE);
} else return(FALSE);
#else
return(ptr->mconveys & MR_ELEC);
#endif
case POISON_RES:
#ifdef DEBUG
if (ptr->mconveys & MR_POISON) {
debugpline("can get poison resistance");
return(TRUE);
} else return(FALSE);
#else
return(ptr->mconveys & MR_POISON);
#endif
case TELEPORT:
#ifdef DEBUG
if (can_teleport(ptr)) {
debugpline("can get teleport");
return(TRUE);
} else return(FALSE);
#else
return(can_teleport(ptr));
#endif
case TELEPORT_CONTROL:
#ifdef DEBUG
if (control_teleport(ptr)) {
debugpline("can get teleport control");
return(TRUE);
} else return(FALSE);
#else
return(control_teleport(ptr));
#endif
case TELEPAT:
#ifdef DEBUG
if (telepathic(ptr)) {
debugpline("can get telepathy");
return(TRUE);
} else return(FALSE);
#else
return(telepathic(ptr));
#endif
default:
return(FALSE);
}
/*NOTREACHED*/
}
/* givit() tries to give you an intrinsic based on the monster's level
* and what type of intrinsic it is trying to give you.
*/
static void
givit(type, ptr)
int type;
register struct permonst *ptr;
{
register int chance;
#ifdef DEBUG
debugpline("Attempting to give intrinsic %d", type);
#endif
/* some intrinsics are easier to get than others */
switch (type) {
case POISON_RES:
if ((ptr == &mons[PM_KILLER_BEE] ||
ptr == &mons[PM_SCORPION]) && !rn2(4))
chance = 1;
else
chance = 15;
break;
case TELEPORT:
chance = 10;
break;
case TELEPORT_CONTROL:
chance = 12;
break;
case TELEPAT:
chance = 1;
break;
default:
chance = 15;
break;
}
if (ptr->mlevel <= rn2(chance))
return; /* failed die roll */
switch (type) {
case FIRE_RES:
#ifdef DEBUG
debugpline("Trying to give fire resistance");
#endif
if(!(HFire_resistance & FROMOUTSIDE)) {
You("feel a momentary chill.");
HFire_resistance |= FROMOUTSIDE;
}
break;
case SLEEP_RES:
#ifdef DEBUG
debugpline("Trying to give sleep resistance");
#endif
if(!(HSleep_resistance & FROMOUTSIDE)) {
You("feel wide awake.");
HSleep_resistance |= FROMOUTSIDE;
}
break;
case COLD_RES:
#ifdef DEBUG
debugpline("Trying to give cold resistance");
#endif
if(!(HCold_resistance & FROMOUTSIDE)) {
You("feel full of hot air.");
HCold_resistance |= FROMOUTSIDE;
}
break;
case DISINT_RES:
#ifdef DEBUG
debugpline("Trying to give disintegration resistance");
#endif
if(!(HDisint_resistance & FROMOUTSIDE)) {
You("feel very firm.");
HDisint_resistance |= FROMOUTSIDE;
}
break;
case SHOCK_RES: /* shock (electricity) resistance */
#ifdef DEBUG
debugpline("Trying to give shock resistance");
#endif
if(!(HShock_resistance & FROMOUTSIDE)) {
Your("health currently feels amplified!");
HShock_resistance |= FROMOUTSIDE;
}
break;
case POISON_RES:
#ifdef DEBUG
debugpline("Trying to give poison resistance");
#endif
if(!(HPoison_resistance & FROMOUTSIDE)) {
You("feel healthy.");
HPoison_resistance |= FROMOUTSIDE;
}
break;
case TELEPORT:
#ifdef DEBUG
debugpline("Trying to give teleport");
#endif
if(!(HTeleportation & FROMOUTSIDE)) {
You("feel very jumpy.");
HTeleportation |= FROMOUTSIDE;
}
break;
case TELEPORT_CONTROL:
#ifdef DEBUG
debugpline("Trying to give teleport control");
#endif
if(!(HTeleport_control & FROMOUTSIDE)) {
You("feel in control of yourself.");
HTeleport_control |= FROMOUTSIDE;
}
break;
case TELEPAT:
#ifdef DEBUG
debugpline("Trying to give telepathy");
#endif
if(!(HTelepat & FROMOUTSIDE)) {
You("feel a %s mental acuity.",
Hallucination ? "normal" : "strange");
HTelepat |= FROMOUTSIDE;
/* If blind, make sure monsters show up. */
if (Blind) see_monsters();
}
break;
default:
#ifdef DEBUG
debugpline("Tried to give an impossible intrinsic");
#endif
break;
}
}
static void
cpostfx(pm) /* called after completely consuming a corpse */
register int pm;
{
register int tmp = 0;
switch(pm) {
case PM_WRAITH:
pluslvl();
break;
#ifdef POLYSELF
case PM_HUMAN_WERERAT:
u.ulycn = PM_WERERAT;
break;
case PM_HUMAN_WEREJACKAL:
u.ulycn = PM_WEREJACKAL;
break;
case PM_HUMAN_WEREWOLF:
u.ulycn = PM_WEREWOLF;
break;
#endif
case PM_NURSE:
u.uhp = u.uhpmax;
flags.botl = 1;
break;
case PM_STALKER:
if(!Invis) {
HInvis = rn1(100, 50);
if(!See_invisible)
newsym(u.ux, u.uy);
} else {
register long oldprop = See_invisible;
if (!(HInvis & INTRINSIC)) You("feel hidden!");
HInvis |= FROMOUTSIDE;
HSee_invisible |= FROMOUTSIDE;
if (!oldprop)
newsym(u.ux, u.uy);
}
/* fall into next case */
case PM_YELLOW_LIGHT:
/* fall into next case */
case PM_GIANT_BAT:
make_stunned(HStun + 30,FALSE);
/* fall into next case */
case PM_BAT:
make_stunned(HStun + 30,FALSE);
break;
case PM_GIANT_MIMIC:
tmp += 10;
/* fall into next case */
case PM_LARGE_MIMIC:
tmp += 20;
/* fall into next case */
case PM_SMALL_MIMIC:
tmp += 20;
if(u.usym == S_HUMAN) {
You("cannot resist the temptation to mimic a pile of gold.");
nomul(-tmp);
afternmv = eatmdone;
if (pl_character[0]=='E')
nomovemsg = "You now again prefer mimicking an elf.";
else
nomovemsg = "You now again prefer mimicking a human.";
u.usym = 0; /* hack! no monster sym 0; use for gold */
newsym(u.ux,u.uy);
}
break;
case PM_QUANTUM_MECHANIC:
Your("velocity suddenly seems very uncertain!");
if (Fast & INTRINSIC) {
Fast &= ~INTRINSIC;
You("seem slower.");
} else {
Fast |= FROMOUTSIDE;
You("seem faster.");
}
break;
case PM_LIZARD:
if (HStun > 2) make_stunned(2L,FALSE);
if (HConfusion > 2) make_confused(2L,FALSE);
break;
case PM_CHAMELEON:
You("feel a change coming over you.");
#ifdef POLYSELF
polyself();
#else
newman();
#endif
break;
case PM_MIND_FLAYER:
if (ABASE(A_INT) < ATTRMAX(A_INT)) {
if (!rn2(2)) {
pline("Yum! That was real brain food!");
(void) adjattrib(A_INT, 1, FALSE);
break; /* don't give them telepathy, too */
}
}
else {
pline("For some reason, that tasted bland.");
}
/* fall through to default case */
default: {
register struct permonst *ptr = &mons[pm];
int i, count;
if(dmgtype(ptr, AD_STUN) || pm==PM_VIOLET_FUNGUS) {
pline ("Oh wow! Great stuff!");
make_hallucinated(HHallucination + 200,FALSE,0L);
}
/* prevent polymorph abuse by killing/eating your offspring */
if(pm >= PM_BABY_GRAY_DRAGON && pm <= PM_BABY_YELLOW_DRAGON)
return;
if(is_giant(ptr)) gainstr((struct obj *)0, 0);
/* Check the monster for all of the intrinsics. If this
* monster can give more than one, pick one to try to give
* from among all it can give.
*
* If a monster can give 4 intrinsics then you have
* a 1/1 * 1/2 * 2/3 * 3/4 = 1/4 chance of getting the first,
* a 1/2 * 2/3 * 3/4 = 1/4 chance of getting the second,
* a 1/3 * 3/4 = 1/4 chance of getting the third,
* and a 1/4 chance of getting the fourth.
*
* And now a proof by induction:
* it works for 1 intrinsic (1 in 1 of getting it)
* for 2 you have a 1 in 2 chance of getting the second,
* otherwise you keep the first
* for 3 you have a 1 in 3 chance of getting the third,
* otherwise you keep the first or the second
* for n+1 you have a 1 in n+1 chance of getting the (n+1)st,
* otherwise you keep the previous one.
* Elliott Kleinrock, October 5, 1990
*/
count = 0; /* number of possible intrinsics */
tmp = 0; /* which one we will try to give */
for (i = 1; i <= LAST_PROP; i++) {
if (intrinsic_possible(i, ptr)) {
count++;
/* a 1 in count chance of replacing the old
* one with this one, and a count-1 in count
* chance of keeping the old one. (note
* that 1 in 1 and 0 in 1 are what we want
* for the first one
*/
if (!rn2(count)) {
#ifdef DEBUG
debugpline("Intrinsic %d replacing %d",
i, tmp);
#endif
tmp = i;
}
}
}
/* if any found try to give them one */
if (count) givit(tmp, ptr);
}
break;
}
return;
}
STATIC_PTR
int
opentin() /* called during each move whilst opening a tin */
{
register int r;
if(!carried(tin.tin) && !obj_here(tin.tin, u.ux, u.uy))
/* perhaps it was stolen? */
return(0); /* %% probably we should use tinoid */
if(tin.usedtime++ >= 50) {
You("give up your attempt to open the tin.");
return(0);
}
if(tin.usedtime < tin.reqtime)
return(1); /* still busy */
if(tin.tin->cursed && tin.tin->spe != -1 && !rn2(8)) {
b_trapped("tin");
goto use_me;
}
You("succeed in opening the tin.");
if(tin.tin->spe != 1) {
if(tin.tin->corpsenm == -1) {
pline("It turns out to be empty.");
tin.tin->dknown = tin.tin->known = TRUE;
goto use_me;
}
r = tin.tin->cursed ? 4 : /* Always rotten if cursed */
(tin.tin->spe == -1) ? 5 : /* "homemade" if player made */
rn2(TTSZ-1); /* else take your pick */
pline("It smells like %s.", makeplural(
Hallucination ? rndmonnam() : mons[tin.tin->corpsenm].mname));
if (yn("Eat it?") == 'n') {
if (!Hallucination) tin.tin->dknown = tin.tin->known = TRUE;
if (flags.verbose) You("discard the open tin.");
goto use_me;
}
You("consume %s %s.", tintxts[r].txt,
mons[tin.tin->corpsenm].mname);
tin.tin->dknown = tin.tin->known = TRUE;
cprefx(tin.tin->corpsenm); cpostfx(tin.tin->corpsenm);
/* check for vomiting added by GAN 01/16/87 */
if(tintxts[r].nut < 0) make_vomiting((long)rn1(15,10), FALSE);
else lesshungry(tintxts[r].nut);
if(r == 0) { /* Deep Fried */
Glib = rnd(15);
pline("Eating deep fried food made your %s very slippery.",
makeplural(body_part(FINGER)));
}
} else {
if (tin.tin->cursed)
pline("It contains some decaying %s substance.",
Hallucination ? hcolor() : green);
else
pline("It contains spinach.");
if (yn("Eat it?") == 'n') {
if (!Hallucination && !tin.tin->cursed)
tin.tin->dknown = tin.tin->known = TRUE;
if (flags.verbose)
You("discard the open tin.");
goto use_me;
}
if (!tin.tin->cursed)
pline("This makes you feel like %s!",
Hallucination ? "Swee'pea" : "Popeye");
lesshungry(600);
gainstr(tin.tin, 0);
}
tin.tin->dknown = tin.tin->known = TRUE;
use_me:
if (carried(tin.tin)) useup(tin.tin);
else useupf(tin.tin);
tin.tin = (struct obj *) 0;
return(0);
}
static void
start_tin(otmp) /* called when starting to open a tin */
register struct obj *otmp;
{
register int tmp;
#ifdef POLYSELF
if (metallivorous(uasmon)) {
You("bite right into the metal tin....");
tmp = 1;
} else
#endif
if (otmp->blessed) {
pline("The tin opens like magic!");
tmp = 1;
} else if(uwep) {
switch(uwep->otyp) {
case TIN_OPENER:
tmp = 1;
break;
case DAGGER:
case ELVEN_DAGGER:
case ORCISH_DAGGER:
case ATHAME:
case CRYSKNIFE:
tmp = 3;
break;
case PICK_AXE:
case AXE:
tmp = 6;
break;
default:
goto no_opener;
}
pline("Using your %s you try to open the tin.",
aobjnam(uwep, NULL));
} else {
no_opener:
pline("It is not so easy to open this tin.");
if(Glib) {
pline("The tin slips out of your hands.");
if(otmp->quan > 1L) {
register struct obj *obj;
obj = splitobj(otmp, 1L);
if(otmp == uwep) setuwep(obj);
}
if (carried(otmp)) dropx(otmp);
else stackobj(otmp);
return;
}
tmp = rn1(1 + 500/((int)(ACURR(A_DEX) + ACURRSTR)), 10);
}
tin.reqtime = tmp;
tin.usedtime = 0;
tin.tin = otmp;
set_occupation(opentin, "opening the tin", 0);
return;
}
int
Hear_again() { /* called when waking up after fainting */
flags.soundok = 1;
return 0;
}
static int
#ifdef POLYSELF
rottenfood(obj)
struct obj *obj;
#else
rottenfood()
#endif
{ /* called on the "first bite" of rotten food */
#ifdef POLYSELF
pline("Blecch! Rotten %s!", foodword(obj));
#else
pline("Blecch! Rotten food!");
#endif
if(!rn2(4)) {
if (Hallucination) You("feel rather trippy.");
else You("feel rather %s.", body_part(LIGHT_HEADED));
make_confused(HConfusion + d(2,4),FALSE);
} else if(!rn2(4) && !Blind) {
pline("Everything suddenly goes dark.");
make_blinded((long)d(2,10),FALSE);
} else if(!rn2(3)) {
if(Blind)
pline("The world spins and you slap against the floor.");
else
pline("The world spins and goes dark.");
flags.soundok = 0;
nomul(-rnd(10));
nomovemsg = "You are conscious again.";
afternmv = Hear_again;
return(1);
}
return(0);
}
static int
eatcorpse(otmp) /* called when a corpse is selected as food */
register struct obj *otmp;
{
register const char *cname = mons[otmp->corpsenm].mname;
register int tp, rotted = 0;
tp = 0;
if(otmp->corpsenm != PM_LIZARD) {
#ifndef LINT /* problem if more than 320K moves before try to eat */
rotted = (monstermoves - otmp->age)/((long)(10 + rn2(20)));
#endif
if(otmp->cursed) rotted += 2;
else if (otmp->blessed) rotted -= 2;
}
if(otmp->corpsenm != PM_ACID_BLOB && (rotted > 5)) {
pline("Ulch - that %s was tainted!",
mons[otmp->corpsenm].mlet == S_FUNGUS ? "fungoid vegetation" :
is_meaty(&mons[otmp->corpsenm]) ? "meat" : "protoplasm");
#ifdef POLYSELF
if (u.usym == S_FUNGUS)
pline("It doesn't seem at all sickening, though...");
else {
#endif
make_sick((long) rn1(10, 10),FALSE);
Sprintf(corpsename, "rotted %s corpse", cname);
u.usick_cause = (const char *)corpsename;
flags.botl = 1;
#ifdef POLYSELF
}
#endif
if (carried(otmp)) useup(otmp);
else useupf(otmp);
return(1);
} else if(acidic(&mons[otmp->corpsenm])
#ifdef POLYSELF
&& !resists_acid(uasmon)
#endif
) {
tp++;
You("have a very bad case of stomach acid.");
losehp(rnd(15), "acidic corpse", KILLED_BY_AN);
} else if(poisonous(&mons[otmp->corpsenm]) && rn2(5)) {
tp++;
pline("Ecch - that must have been poisonous!");
if(!Poison_resistance) {
losestr(rnd(4));
losehp(rnd(15), "poisonous corpse", KILLED_BY_AN);
} else You("seem unaffected by the poison.");
/* now any corpse left too long will make you mildly ill */
} else if(((rotted > 5) || ((rotted > 3) && rn2(5)))
#ifdef POLYSELF
&& u.usym != S_FUNGUS
#endif
){
tp++;
You("feel%s sick.", (Sick) ? " very" : "");
losehp(rnd(8), "cadaver", KILLED_BY_AN);
}
if(!tp && otmp->corpsenm != PM_LIZARD && (otmp->orotten || !rn2(7))) {
#ifdef POLYSELF
if(rottenfood(otmp)) {
#else
if(rottenfood()) {
#endif
otmp->orotten = TRUE;
(void)touchfood(otmp);
return(1);
}
otmp->oeaten >>= 2;
} else {
#ifdef POLYSELF
pline("This %s corpse %s!", cname,
(carnivorous(uasmon) && !herbivorous(uasmon))
? "is delicious" : "tastes terrible");
#else
pline("This %s corpse tastes terrible!", cname);
#endif
}
/* delay is weight dependent */
victual.reqtime = 3 + (mons[otmp->corpsenm].cwt >> 6);
return(0);
}
static void
start_eating(otmp) /* called as you start to eat */
register struct obj *otmp;
{
#ifdef DEBUG
debugpline("start_eating: %lx (victual = %lx)", otmp, victual.piece);
debugpline("reqtime = %d", victual.reqtime);
debugpline("(original reqtime = %d)", objects[otmp->otyp].oc_delay);
debugpline("nmod = %d", victual.nmod);
debugpline("oeaten = %d", otmp->oeaten);
#endif
victual.fullwarn = victual.doreset = FALSE;
victual.eating = TRUE;
if (otmp->otyp == CORPSE) {
cprefx(victual.piece->corpsenm);
if (!victual.piece && victual.eating) do_reset_eat();
if (victual.eating == FALSE) return; /* died and lifesaved */
}
if (bite()) return;
if(++victual.usedtime >= victual.reqtime) {
/* print "finish eating" message if they just resumed -dlc */
done_eating(victual.reqtime > 1 ? TRUE : FALSE);
return;
}
Sprintf(msgbuf, "eating %s", the(singular(otmp, xname)));
set_occupation(eatfood, msgbuf, 0);
}
static void
fprefx(otmp) /* called on "first bite" of (non-corpse) food */
register struct obj *otmp;
{
switch(otmp->otyp) {
case FOOD_RATION:
if(u.uhunger <= 200)
if (Hallucination) pline("Oh wow, like, superior, man!");
else pline("That food really hit the spot!");
else if(u.uhunger <= 700) pline("That satiated your stomach!");
break;
case TRIPE_RATION:
#ifdef POLYSELF
if (carnivorous(uasmon) && !humanoid(uasmon))
pline("That tripe ration was surprisingly good!");
else {
#endif
pline("Yak - dog food!");
more_experienced(1,0);
flags.botl = 1;
#ifdef POLYSELF
}
#endif
if(rn2(2)
#ifdef POLYSELF
&& (!carnivorous(uasmon) || humanoid(uasmon))
#endif
) {
make_vomiting((long)rn1(victual.reqtime, 14), FALSE);
}
break;
#ifdef POLYSELF
case CLOVE_OF_GARLIC:
if (is_undead(uasmon)) {
make_vomiting((long)rn1(victual.reqtime, 5), FALSE);
break;
}
/* Fall through otherwise */
#endif
default:
#ifdef TUTTI_FRUTTI
if (otmp->otyp==SLIME_MOLD && !otmp->cursed
&& otmp->spe == current_fruit)
pline("My, that was a yummy %s!", singular(otmp, xname));
else
#endif
#ifdef UNIX
if (otmp->otyp == APPLE || otmp->otyp == PEAR) {
if (!Hallucination) pline("Core dumped.");
else {
/* This is based on an old Usenet joke, a fake a.out manual page */
int x = rnd(100);
if (x <= 75)
pline("Segmentation fault -- core dumped.");
else if (x <= 99)
pline("Bus error -- core dumped.");
else pline("Yo' mama -- core dumped.");
}
} else
#endif
pline("This %s is %s!", singular(otmp, xname),
otmp->cursed ? (Hallucination ? "grody" : "terrible"):
Hallucination ? "gnarly" :
(otmp->otyp==CRAM_RATION ? "bland" : "delicious"));
break;
}
}
#ifdef POLYSELF
static void
eatring(otmp)
struct obj *otmp;
{
int typ = otmp->otyp;
int oldprop;
/* Note: rings are not so common that this is unbalancing. (How */
/* often do you even _find_ 3 rings of polymorph in a game? */
oldprop = !!(u.uprops[objects[typ].oc_oprop].p_flgs);
otmp->known = otmp->dknown = 1; /* by taste */
if (!rn2(3)) switch (otmp->otyp) {
case RIN_AGGRAVATE_MONSTER:
case RIN_WARNING:
case RIN_POISON_RESISTANCE:
case RIN_FIRE_RESISTANCE:
case RIN_COLD_RESISTANCE:
case RIN_SHOCK_RESISTANCE:
case RIN_TELEPORTATION:
case RIN_TELEPORT_CONTROL:
case RIN_SEE_INVISIBLE:
case RIN_PROTECTION_FROM_SHAPE_CHAN:
case RIN_SEARCHING:
case RIN_STEALTH:
case RIN_INVISIBILITY:
case RIN_CONFLICT:
case RIN_POLYMORPH:
case RIN_POLYMORPH_CONTROL:
case RIN_REGENERATION: /* Probably stupid. */
case RIN_HUNGER: /* Stupid. */
case RIN_LEVITATION: /* Stupid. */
if (!(u.uprops[objects[typ].oc_oprop].p_flgs & FROMOUTSIDE))
pline("Magic spreads through your body as you digest the ring.");
u.uprops[objects[typ].oc_oprop].p_flgs |= FROMOUTSIDE;
if (typ == RIN_SEE_INVISIBLE) {
set_mimic_blocking();
see_monsters();
if (Invis && !oldprop
#ifdef POLYSELF
&& !perceives(uasmon)
#endif
&& !Blind) {
newsym(u.ux,u.uy);
pline("Suddenly you can see yourself.");
makeknown(typ);
}
} else if (typ == RIN_INVISIBILITY) {
if (!oldprop && !See_invisible && !Blind) {
newsym(u.ux,u.uy);
Your("body takes on a %s transparency...",
Hallucination ? "normal" : "strange");
makeknown(typ);
}
} else if (typ == RIN_PROTECTION_FROM_SHAPE_CHAN)
rescham();
else if (typ == RIN_LEVITATION) {
if (!Levitation) {
float_up();
makeknown(typ);
}
}
break;
case RIN_ADORNMENT:
if (adjattrib(A_CHA, otmp->spe, -1))
makeknown(typ);
break;
case RIN_GAIN_STRENGTH:
case RIN_INCREASE_DAMAGE: /* Any better ideas? */
if (adjattrib(A_STR, otmp->spe, -1))
makeknown(typ);
break;
case RIN_PROTECTION:
Protection |= FROMOUTSIDE;
u.ublessed += otmp->spe;
flags.botl = 1;
break;
}
}
static void
eatspecial() /* called after eating non-food */
{
register struct obj *otmp = victual.piece;
lesshungry(victual.nmod);
victual.piece = (struct obj *)0;
victual.eating = 0;
if (otmp->oclass == GOLD_CLASS) {
dealloc_obj(otmp);
return;
}
if (otmp->oclass == POTION_CLASS) {
otmp->quan++; /* dopotion() does a useup() */
(void)dopotion(otmp);
}
if (otmp->oclass == RING_CLASS)
eatring(otmp);
if (otmp == uball) unpunish();
if (otmp == uchain) unpunish(); /* but no useup() */
else if (carried(otmp)) useup(otmp);
else useupf(otmp);
}
/* NOTE: the order of these words exactly corresponds to the
order of oc_material values #define'd in objclass.h. */
static const char *foodwords[] = {
"meal", "liquid", "wax", "food", "meat",
"paper", "cloth", "leather", "wood", "bone", "scale",
"metal", "metal", "metal", "silver", "gold", "platinum", "mithril",
"plastic", "glass", "rich food", "stone"
};
static const char *
foodword(otmp)
register struct obj *otmp;
{
if (otmp->oclass == FOOD_CLASS) return "food";
if (otmp->oclass == GEM_CLASS &&
objects[otmp->otyp].oc_material == GLASS &&
otmp->dknown)
makeknown(otmp->otyp);
return foodwords[objects[otmp->otyp].oc_material];
}
#endif
static void
fpostfx(otmp) /* called after consuming (non-corpse) food */
register struct obj *otmp;
{
switch(otmp->otyp) {
#ifdef POLYSELF
case SPRIG_OF_WOLFSBANE:
if (u.ulycn != -1) {
u.ulycn = -1;
You("feel purified.");
if(uasmon == &mons[u.ulycn] && !Polymorph_control)
rehumanize();
}
break;
#endif
case CARROT:
make_blinded(0L,TRUE);
break;
case FORTUNE_COOKIE:
outrumor(bcsign(otmp), TRUE);
break;
case LUMP_OF_ROYAL_JELLY:
/* This stuff seems to be VERY healthy! */
gainstr(otmp, 1);
u.uhp += (otmp->cursed) ? -rnd(20) : rnd(20);
if(u.uhp > u.uhpmax) {
if(!rn2(17)) u.uhpmax++;
u.uhp = u.uhpmax;
} else if(u.uhp <= 0) {
killer_format = KILLED_BY_AN;
killer = "rotten lump of royal jelly";
done(POISONING);
}
if(!otmp->cursed) heal_legs();
break;
case EGG:
if(otmp->corpsenm == PM_COCKATRICE) {
#ifdef POLYSELF
if(!resists_ston(uasmon))
if(!poly_when_stoned(uasmon) ||
!polymon(PM_STONE_GOLEM))
{
#endif
if (!Stoned) Stoned = 5;
killer_format = KILLED_BY_AN;
killer = "cockatrice egg";
#ifdef POLYSELF
}
#endif
}
break;
}
return;
}
int
doeat() /* generic "eat" command funtion (see cmd.c) */
{
register struct obj *otmp;
int basenutrit; /* nutrition of full item */
if (Strangled) {
pline("If you can't breathe air, how can you consume solids?");
return 0;
}
if (!(otmp = floorfood("eat", 0))) return 0;
if (check_capacity(NULL)) return 0;
#ifdef POLYSELF
/* We have to make non-foods take no time to eat, unless we want to
* do ridiculous amounts of coding to deal with partly eaten plate
* mails, players who polymorph back to human in the middle of their
* metallic meal, etc....
*/
if (!is_edible(otmp)) {
You("cannot eat that!");
return 0;
}
if (otmp->oclass != FOOD_CLASS) {
victual.reqtime = 1;
victual.piece = otmp;
/* Don't split it, we don't need to if it's 1 move */
victual.usedtime = 0;
victual.canchoke = (u.uhs == SATIATED);
/* Note: gold weighs 1 pt. for each 1000 pieces (see */
/* pickup.c) so gold and non-gold is consistent. */
if (otmp->oclass == GOLD_CLASS)
basenutrit = ((otmp->quan > 200000L) ? 2000
: (int)(otmp->quan/100L));
else basenutrit = objects[otmp->otyp].oc_nutrition;
victual.nmod = basenutrit;
victual.eating = TRUE; /* needed for lesshungry() */
if (otmp->cursed)
(void) rottenfood(otmp);
if (otmp->oclass == WEAPON_CLASS && otmp->opoisoned) {
pline("Ecch - that must have been poisonous!");
if(!Poison_resistance) {
losestr(rnd(4));
losehp(rnd(15), xname(otmp), KILLED_BY_AN);
} else
You("seem unaffected by the poison.");
} else if (!otmp->cursed)
pline("This %s is delicious!",
otmp->oclass == GOLD_CLASS ? foodword(otmp) :
singular(otmp, xname));
eatspecial();
return 1;
}
#endif
if(otmp == victual.piece) {
/* If they weren't able to choke, they don't suddenly become able to
* choke just because they were interrupted. On the other hand, if
* they were able to choke before, if they lost food it's possible
* they shouldn't be able to choke now.
*/
if (u.uhs != SATIATED) victual.canchoke = FALSE;
if(!carried(victual.piece)) {
if(victual.piece->quan > 1L)
(void) splitobj(victual.piece, 1L);
}
You("resume your meal.");
start_eating(victual.piece);
return(1);
}
/* nothing in progress - so try to find something. */
/* tins are a special case */
if(otmp->otyp == TIN) {
start_tin(otmp);
return(1);
}
victual.piece = otmp = touchfood(otmp);
victual.usedtime = 0;
/* Now we need to calculate delay and nutritional info.
* The base nutrition calculated here and in eatcorpse() accounts
* for normal vs. rotten food. The reqtime and nutrit values are
* then adjusted in accordance with the amount of food left.
*/
if(otmp->otyp == CORPSE) {
if(eatcorpse(otmp)) return(1);
/* else eatcorpse sets up reqtime and oeaten */
} else {
victual.reqtime = objects[otmp->otyp].oc_delay;
if (otmp->otyp != FORTUNE_COOKIE &&
(otmp->cursed ||
(((monstermoves - otmp->age) > (int) otmp->blessed ? 50:30) &&
(otmp->orotten || !rn2(7))))) {
#ifdef POLYSELF
if(rottenfood(otmp)) {
#else
if(rottenfood()) {
#endif
otmp->orotten = TRUE;
return(1);
}
otmp->oeaten >>= 1;
} else fprefx(otmp);
}
/* re-calc the nutrition */
if (otmp->otyp == CORPSE) basenutrit = mons[otmp->corpsenm].cnutrit;
else basenutrit = objects[otmp->otyp].oc_nutrition;
#ifdef DEBUG
debugpline("before rounddiv: victual.reqtime == %d", victual.reqtime);
debugpline("oeaten == %d, basenutrit == %d", otmp->oeaten, basenutrit);
#endif
victual.reqtime = (basenutrit == 0 ? 0 :
rounddiv(victual.reqtime * (long)otmp->oeaten, basenutrit));
#ifdef DEBUG
debugpline("after rounddiv: victual.reqtime == %d", victual.reqtime);
#endif
/* calculate the modulo value (nutrit. units per round eating)
* note: this isn't exact - you actually lose a little nutrition
* due to this method.
* TODO: add in a "remainder" value to be given at the end of the
* meal.
*/
if(victual.reqtime == 0)
/* possible if most has been eaten before */
victual.nmod = 0;
else if (otmp->oeaten > victual.reqtime)
victual.nmod = -(otmp->oeaten / victual.reqtime);
else
victual.nmod = victual.reqtime % otmp->oeaten;
victual.canchoke = (u.uhs == SATIATED);
start_eating(otmp);
return(1);
}
/* Take a single bite from a piece of food, checking for choking and
* modifying usedtime. Returns 1 if they choked and survived, 0 otherwise.
*/
static int
bite()
{
if(victual.canchoke && u.uhunger >= 2000) {
choke(victual.piece);
return 1;
}
if (victual.doreset) {
do_reset_eat();
return 0;
}
if(victual.nmod < 0) {
lesshungry(-victual.nmod);
victual.piece->oeaten -= -victual.nmod;
} else if(victual.nmod > 0 && (victual.usedtime % victual.nmod)) {
lesshungry(1);
victual.piece->oeaten--;
}
recalc_wt();
return 0;
}
#endif /* OVLB */
#ifdef OVL0
void
gethungry() /* as time goes by - called by moveloop() and domove() */
{
if (u.uinvulnerable) return; /* you don't feel hungrier */
if ((!u.usleep || !rn2(10)) /* slow metabolic rate while asleep */
&& (carnivorous(uasmon) || herbivorous(uasmon)))
u.uhunger--; /* ordinary food consumption */
if (moves % 2) { /* odd turns */
/* Regeneration uses up food, unless due to an artifact */
if ((HRegeneration & (~W_ART)) &&
(HRegeneration != W_WEP || !uwep->oartifact)) u.uhunger--;
if (near_capacity() > SLT_ENCUMBER) u.uhunger--;
} else { /* even turns */
if (Hunger) u.uhunger--;
/* Conflict uses up food too */
if ((Conflict & (~W_ARTI))) u.uhunger--;
/* +0 charged rings don't do anything, so don't affect hunger */
switch (moves % 20) { /* note: use even cases only */
case 4: if (uleft &&
(uleft->spe || !objects[uleft->otyp].oc_charged))
u.uhunger--;
break;
case 8: if (uamul) u.uhunger--;
break;
case 12: if (uright &&
(uright->spe || !objects[uright->otyp].oc_charged))
u.uhunger--;
break;
case 16: if (u.uhave.amulet) u.uhunger--;
break;
default: break;
}
}
newuhs(TRUE);
}
#endif /* OVL0 */
#ifdef OVLB
void
morehungry(num) /* called after vomiting and after performing feats of magic */
register int num;
{
u.uhunger -= num;
newuhs(TRUE);
}
void
lesshungry(num) /* called after eating (and after drinking fruit juice) */
register int num;
{
#ifdef DEBUG
debugpline("lesshungry(%d)", num);
#endif
u.uhunger += num;
if(u.uhunger >= 2000) {
if (!victual.eating || victual.canchoke)
if (victual.eating) {
choke(victual.piece);
reset_eat();
} else
if (tin.tin)
choke(tin.tin);
else
choke((struct obj *) 0);
/* no reset_eat(); it was a non-food such as juice */
} else {
/* Have lesshungry() report when you're nearly full so all eating
* warns when you're about to choke.
*/
if (u.uhunger >= 1500) {
if(!victual.eating || (victual.eating && !victual.fullwarn)) {
pline("You're having a hard time getting all of it down.");
nomovemsg = "You're finally finished.";
if(!victual.eating)
multi = -2;
else {
victual.fullwarn = TRUE;
if (victual.canchoke &&
/* a one-gulp food will not survive a stop */
objects[victual.piece->otyp].oc_delay > 1) {
if(yn("Stop eating?") == 'y')
{
reset_eat();
nomovemsg = NULL;
}
}
}
}
}
}
newuhs(FALSE);
}
STATIC_PTR
int
unfaint() {
(void) Hear_again();
if(u.uhs > FAINTING)
u.uhs = FAINTING;
stop_occupation();
flags.botl = 1;
return 0;
}
#endif /* OVLB */
#ifdef OVL0
boolean
is_fainted() {
return(u.uhs == FAINTED);
}
void
reset_faint() { /* call when a faint must be prematurely terminated */
if(is_fainted()) nomul(0);
}
#if 0
void
sync_hunger() {
if(is_fainted()) {
flags.soundok = 0;
nomul(-10+(u.uhunger/10));
nomovemsg = "You regain consciousness.";
afternmv = unfaint;
}
}
#endif
void
newuhs(incr) /* compute and comment on your (new?) hunger status */
boolean incr;
{
register int newhs, h = u.uhunger;
newhs = (h > 1000) ? SATIATED :
(h > 150) ? NOT_HUNGRY :
(h > 50) ? HUNGRY :
(h > 0) ? WEAK : FAINTING;
if(newhs == FAINTING) {
if(is_fainted()) newhs = FAINTED;
if(u.uhs <= WEAK || rn2(20-u.uhunger/10) >= 19) {
if(!is_fainted() && multi >= 0 /* %% */) {
/* stop what you're doing, then faint */
stop_occupation();
You("faint from lack of food.");
flags.soundok = 0;
nomul(-10+(u.uhunger/10));
nomovemsg = "You regain consciousness.";
afternmv = unfaint;
newhs = FAINTED;
}
} else
if(u.uhunger < -(int)(200 + 20*ACURR(A_CON))) {
u.uhs = STARVED;
flags.botl = 1;
bot();
You("die from starvation.");
killer_format = KILLED_BY;
killer = "starvation";
done(STARVING);
/* if we return, we lifesaved, and that calls newuhs */
return;
}
}
if(newhs != u.uhs) {
if(newhs >= WEAK && u.uhs < WEAK)
losestr(1); /* this may kill you -- see below */
else if(newhs < WEAK && u.uhs >= WEAK)
losestr(-1);
switch(newhs){
case HUNGRY:
if (Hallucination) {
pline((!incr) ?
"You now have a lesser case of the munchies." :
"You are getting the munchies.");
} else
You((!incr) ? "only feel hungry now." :
(u.uhunger < 145) ? "feel hungry." :
"are beginning to feel hungry.");
if (incr && occupation &&
(occupation != eatfood && occupation != opentin))
stop_occupation();
break;
case WEAK:
if (Hallucination)
pline((!incr) ?
"You still have the munchies." :
"The munchies are interfering with your motor capabilities.");
else
You((!incr) ? "feel weak now." :
(u.uhunger < 45) ? "feel weak." :
"are beginning to feel weak.");
if (incr && occupation &&
(occupation != eatfood && occupation != opentin))
stop_occupation();
break;
}
u.uhs = newhs;
flags.botl = 1;
if(u.uhp < 1) {
You("die from hunger and exhaustion.");
killer_format = KILLED_BY;
killer = "exhaustion";
done(STARVING);
return;
}
}
}
#endif /* OVL0 */
#ifdef OVLB
/* Returns an object representing food. Object may be either on floor or
* in inventory.
*/
struct obj *
floorfood(verb,corpseonly) /* get food from floor or pack */
const char *verb;
boolean corpseonly;
{
register struct obj *otmp;
char qbuf[QBUFSZ];
char c;
#ifdef POLYSELF
struct obj *gold = g_at(u.ux, u.uy);
boolean feeding = (!strcmp(verb, "eat"));
if (feeding && gold && metallivorous(uasmon)) {
if (gold->quan == 1L)
Sprintf(qbuf, "There is 1 gold piece here; eat it?");
else Sprintf(qbuf, "There are %ld gold pieces here; eat them?",
gold->quan);
if (yn(qbuf) == 'y') {
/* tricky, because gold isn't a real object -dlc */
freeobj(gold);
return gold;
}
}
#endif
/* Is there some food (probably a heavy corpse) here on the ground? */
if (!(Levitation && !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz))
&& !u.uswallow) {
for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) {
if(corpseonly ? otmp->otyp==CORPSE :
#ifdef POLYSELF
feeding ? (otmp->oclass != GOLD_CLASS && is_edible(otmp)) :
#endif
otmp->oclass==FOOD_CLASS) {
Sprintf(qbuf, "There %s %s here; %s %s?",
(otmp->quan == 1L) ? "is" : "are",
doname(otmp), verb,
(otmp->quan == 1L) ? "it" : "one");
if((c = yn_function(qbuf,ynqchars,'n')) == 'y')
return(otmp);
else if(c == 'q')
return((struct obj *) 0);
}
}
}
#ifdef POLYSELF
/* We cannot use ALL_CLASSES since that causes getobj() to skip its
* "ugly checks" and we need to check for inedible items.
*/
return getobj(feeding ? (const char *)allobj :
(const char *)comestibles, verb);
#else
return getobj(comestibles, verb);
#endif
}
/* Side effects of vomiting */
/* added nomul (MRS) - it makes sense, you're too busy being sick! */
/* TO DO: regurgitate swallowed monsters when poly'd */
void
vomit() { /* A good idea from David Neves */
make_sick(0L,TRUE);
nomul(-2);
}
int
eaten_stat(base, obj)
register int base;
register struct obj *obj;
{
long uneaten_amt, full_amount;
uneaten_amt = (long)obj->oeaten;
full_amount = (obj->otyp == CORPSE) ? (long)mons[obj->corpsenm].cnutrit
: (long)objects[obj->otyp].oc_nutrition;
base = (int)(full_amount ? (long)base * uneaten_amt / full_amount : 0L);
return (base < 1) ? 1 : base;
}
#endif /* OVLB */
/*eat.c*/